/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "DataSourceWaveFile.h"
#include <resource/Manager.h>
#include "PCMConvert.h"
#include "xm/OrderedReader.h"

#include <boost/scoped_ptr.hpp>

namespace liba
{
namespace noise
{

#pragma pack( push, 1 )
struct WAV
{
	char  rID[4];
	unsigned rLen;
	char  wID[4];
	char  fID[4];
	unsigned fLen;
	unsigned short wFormatTag;
	unsigned short nChannels;
	unsigned nSamplesPerSec;
	unsigned nAvgBytesPerSec;
	unsigned short nBlockAlign;
	unsigned short wBitsPerSample;
};
#pragma pack( pop )

WaveFileRep::WaveFileRep( const filesystem::String & file_name )
:	valid_flag( false )
{
	boost::scoped_ptr<resource::Resource> res( resource::Manager::create_resource( file_name + ".wav" ) );
	if( !res )
		return;
	WAV info;
	/* read wav header */
	res->read( info.rID, 4 );
	info.rLen = read_long_little_endian( res.get() );
	res->read( info.wID, 4 );
	res->read( info.fID, 4 );
	info.fLen = read_long_little_endian( res.get() );
	info.wFormatTag = read_short_little_endian( res.get() );
	info.nChannels = read_short_little_endian( res.get() );
	info.nSamplesPerSec = read_long_little_endian( res.get() );
	info.nAvgBytesPerSec = read_long_little_endian( res.get() );
	info.nBlockAlign = read_short_little_endian( res.get() );
	info.wBitsPerSample = read_short_little_endian( res.get() );

	/* check it */
	if( memcmp(info.rID,"RIFF",4) ||
		memcmp(info.wID,"WAVE",4) ||
		memcmp(info.fID,"fmt ",4) )
	{
		return;
	}

	/* skip other crap */
	if( res->skip( info.fLen - 16 ) != info.fLen - 16 )
		return;

	int chunk_length = 0;
	char dID[4] = {0, 0, 0, 0};
	while( memcmp(dID,"data",4) )
	{
		if( res->skip( chunk_length ) != chunk_length )
			return;
		if( res->read( dID, 4 ) != 4 )
			return;
		chunk_length = read_long_little_endian( res.get() );
	}
//	chunk_length -= 8;

	wave_data.resize( chunk_length );
	if( info.wBitsPerSample == 16 )
	{
		for( int i = 0; i < chunk_length/2; ++i )
			((short int *)&(wave_data.front()))[i] = read_short_little_endian( res.get() );
	}
	else
	{
		if( res->read( &(wave_data.front()), chunk_length ) != chunk_length )
			return;
	}
	src_bits = info.wBitsPerSample;
	src_stride = info.nBlockAlign;
	src_bytes = info.nBlockAlign / info.nChannels;
	channels_count = info.nChannels;
	frequency = info.nSamplesPerSec;
	valid_flag = true;
}

WaveFileRep::~WaveFileRep()
{}

bool WaveFileRep::has_native(DataSource::Type channel)
{
	if( channels_count == 1 )
		return channel == DataSource::MONO;
	if( channels_count == 2 )
		return channel == DataSource::STEREO_LEFT || channel == DataSource::STEREO_RIGHT;
	return false;
}

void WaveFileRep::get_data(int current_sample, int next_sample, DataSource::Type channel, void * dst, int dst_stride, int dst_bits)
{
	int chan = 0;
	if( channels_count == 1 )
	{
		chan = 0;
	}
	if( channels_count == 2 )
	{
		switch( channel )
		{
		case DataSource::MONO:
		case DataSource::STEREO_LEFT:
		case DataSource::SURROUND_FRONTLEFT:
		case DataSource::SURROUND_REARLEFT:
		case DataSource::SURROUND_SUBWOOFER:
		case DataSource::SURROUND_CENTER:
			chan = 0;
			break;
		case DataSource::STEREO_RIGHT:
		case DataSource::SURROUND_FRONTRIGHT:
		case DataSource::SURROUND_REARRIGHT:
			chan = 1;
			break;
		}
	}
	if( chan < 0 )
		return pcm_write_silence( dst, dst_stride, dst_bits, (dst_bits + 7) / 8, next_sample - current_sample );
	return pcm_bit_convert( chan * src_bytes + &(wave_data.front()) + src_stride * current_sample, src_stride, src_bits, src_bytes,
	dst, dst_stride, dst_bits, (dst_bits + 7) / 8, next_sample - current_sample );
	// info.nChannels == 6
	/*switch( channel )
	{
	case DataSource::MONO:
	case DataSource::STEREO_LEFT:
	case DataSource::STEREO_RIGHT:
	case DataSource::SURROUND_FRONTLEFT:
	case DataSource::SURROUND_FRONTRIGHT:
	case DataSource::SURROUND_REARLEFT:
	case DataSource::SURROUND_REARRIGHT:
	case DataSource::SURROUND_SUBWOOFER:
	case DataSource::SURROUND_CENTER:
	}*/
}

} // namespace noise
} // namesapce liba

